package controller

import (
	"database/sql"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/e421083458/golang_common/lib"
	"github.com/e421083458/golang_common/log"
	"github.com/e421083458/gorm"
	"github.com/gin-gonic/gin"
	"github.com/go-redis/redis"
	"github.com/gogf/gf/container/gtype"
	"github.com/gogf/gf/os/glog"
	"github.com/gogf/gf/text/gstr"
	"github.com/gogf/gf/util/gconv"
	"github.com/mitchellh/mapstructure"
	"golang.org/x/text/encoding/simplifiedchinese"
	"golang.org/x/text/transform"
	"io/ioutil"
	"net/http"
	"net/url"
	"regexp"
	"rock/customer/dto"
	"rock/customer/initParam"
	"rock/customer/middleware"
	"rock/customer/models"
	"rock/customer/modules/cacheMap"
	"rock/customer/modules/redisModules"
	"rock/customer/modules/taskMap"
	"rock/customer/public"
	service "rock/customer/service/master"
	"strconv"
	"strings"
	"time"
)

// 事务回滚
func Rollback(path string, transaction *gorm.DB, err error) {
	log.Error(fmt.Sprintf("【PATH】%v【MYSQL ERROR】%v", path, err.Error()))
	transaction.Rollback()
}

// 事务回滚，title自定义
func RollbackOtherTitle(path string, title string, transaction *gorm.DB, err error) {
	log.Error(fmt.Sprintf("【PATH】%v%v%v", path, title, err.Error()))
	transaction.Rollback()
}

// 查询是否有客服机器人群，没有则创建
func checkGroupRACs(router *gin.Context, userId string) error {
	// 查询机器人信息
	robot, err := robotService.Robot(router, BusinessDB)
	if err != nil {
		return err
	}
	// 查询机器人群聊是否存在
	groupCs, err := groupService.GroupCs(router, BusinessDB, userId, "0")
	if err != nil {
		return err
	}
	// 用户id
	if groupCs.UserId > 0 {
		return nil
	}
	// 用户信息
	csUser, err := customerServiceService.CsUser(router, InstanceDB, groupCs.UserId)
	if err != nil {
		return err
	}
	//
	groupId := public.GenID()
	err = robotMsgTransaction(router, groupId, robot, csUser)
	if err != nil {
		return err
	}
	// 发送客服欢迎语
	defer func() {
		err = send(dto.Group{
			GroupId: groupId,
			MsgType: 17, //
		})
		if err != nil {
			public.ContextNotice(router, "error", map[string]interface{}{
				"title": router.Request.URL,
				"msg":   initParam.PublicHint,
				"err":   err,
			})
		}
	}()
	return nil
}

// 获取融云token
func getRonCloudToken(rcParam string) (dto.RongCloudTokenOutput, error) {
	reData := dto.RongCloudTokenOutput{}
	resp, err := public.RongCloudRequest("/user/getToken.json", rcParam)
	if err != nil {
		return reData, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	rcReq := &dto.RongCloudReq{}
	err = json.Unmarshal(body, rcReq)
	if err != nil {
		return reData, err
	}
	if rcReq.Code != 200 {
		reason := fmt.Sprintf("code is %d", rcReq.Code)
		return reData, errors.New(reason)
	}
	reData.Token = rcReq.Token
	reData.UserId = rcReq.UserId
	return reData, nil
}

// 查询用户融云群聊
func queryRonGroup(rcParam string) (dto.GroupRongCloud, error) {
	rcReq := dto.GroupRongCloud{}
	resp, err := public.RongCloudRequest("/user/group/query.json", rcParam)
	if err != nil {
		return rcReq, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	err = json.Unmarshal(body, &rcReq)
	if err != nil {
		return rcReq, err
	}
	if rcReq.Code != 200 {
		errorReq := &public.RongCloudComResponse{}
		err = json.Unmarshal(body, &errorReq)
		if err != nil {
			reason := fmt.Sprintf("rongcloud error code is %d ", rcReq.Code)
			return rcReq, errors.New(reason + err.Error())
		}
		reason := fmt.Sprintf("rongcloud error code is %d errorMsg is %s", errorReq.Code, errorReq.ErrorMessage)
		return rcReq, errors.New(reason)
	}
	return rcReq, nil
}

// 刷新融云群组信息
func refreshRonGroup(rcParam string) (dto.GroupRongCloud, error) {
	rcReq := dto.GroupRongCloud{}
	resp, err := public.RongCloudRequest("/group/refresh.json", rcParam)
	if err != nil {
		return rcReq, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	err = json.Unmarshal(body, &rcReq)
	if err != nil {
		return rcReq, err
	}
	if rcReq.Code != 200 {
		errorReq := &public.RongCloudComResponse{}
		err = json.Unmarshal(body, &errorReq)
		if err != nil {
			reason := fmt.Sprintf("rongcloud error code is %d ", rcReq.Code)
			return rcReq, errors.New(reason + err.Error())
		}
		reason := fmt.Sprintf("rongcloud error code is %d errorMsg is %s", errorReq.Code, errorReq.ErrorMessage)
		return rcReq, errors.New(reason)
	}
	return rcReq, nil
}

// 调用融云发送消息接口
func sendRonCloud(rcParam string) error {
	resp, err := public.RongCloudRequest("/message/group/publish.json", rcParam)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	rcReq := &public.RongCloudComResponse{}
	err = json.Unmarshal(body, &rcReq)
	if err != nil {
		return err
	}
	if rcReq.Code != 200 {
		reason := fmt.Sprintf("rongcloud error code is %d errorMsg is %s rcParam is %s", rcReq.Code, rcReq.ErrorMessage, rcParam)
		return errors.New(reason)
	}
	return nil
}

// 暂时更新融云用户信息; isUpdate 该模式下用户只在第一次更新一次信息
func userRefreshRonCloud(userId string, rcParam string) error {
	isUpdateCache := cacheMap.UpdateUserRefreshRonCloud.Get(userId)
	isUpdate := 1
	if isUpdateCache == nil {
		cacheMap.UpdateUserRefreshRonCloud.Set(userId, 1)
		isUpdate = 0
	}
	if isUpdate == 0 {
		resp, err := public.RongCloudRequest("/user/refresh.json", rcParam)
		if err != nil {
			return err
		}
		defer resp.Body.Close()
		body, err := ioutil.ReadAll(resp.Body)
		rcReq := &public.RongCloudComResponse{}
		err = json.Unmarshal(body, &rcReq)
		if err != nil {
			return err
		}
		if rcReq.Code != 200 {
			if rcReq.Code == 1002 && strings.Contains(rcReq.ErrorMessage, "is not exist") {
				_, err = getRonCloudToken(rcParam)
				if err != nil {
					return err
				}
			} else {
				reason := fmt.Sprintf("rongcloud error code is %d errorMsg is %s rcParam is %s", rcReq.Code, rcReq.ErrorMessage, rcParam)
				return errors.New(reason)
			}
		}
	}
	return nil
}

// 调用融云创建群聊接口
func createGroupRonCloud(rcParam string) error {
	resp, err := public.RongCloudRequest("/group/create.json", rcParam)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	rcReq := &dto.RongCloud{}
	err = json.Unmarshal(body, rcReq)
	if err != nil {
		return err
	}
	if rcReq.Code != 200 {
		errorReq := &public.RongCloudComResponse{}
		err = json.Unmarshal(body, &errorReq)
		if err != nil {
			reason := fmt.Sprintf("rongcloud error code is %d ", rcReq.Code)
			return errors.New(reason)
		}
		reason := fmt.Sprintf("rongcloud error code is %d errorMsg is %s", errorReq.Code, errorReq.ErrorMessage)
		return errors.New(reason)
	}
	return err
}

// 调用融云创建群聊接口
func dismissGroupRonCloud(rcParam string) error {
	resp, err := public.RongCloudRequest("/group/dismiss.json", rcParam)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	rcReq := &dto.RongCloud{}
	err = json.Unmarshal(body, rcReq)
	if err != nil {
		return err
	}
	if rcReq.Code != 200 {
		errorReq := &public.RongCloudComResponse{}
		err = json.Unmarshal(body, &errorReq)
		if err != nil {
			reason := fmt.Sprintf("rongcloud error code is %d ", rcReq.Code)
			return errors.New(reason)
		}
		reason := fmt.Sprintf("rongcloud error code is %d errorMsg is %s", errorReq.Code, errorReq.ErrorMessage)
		return errors.New(reason)
	}
	return err
}

// 调用融云创建群聊接口
func joinGroupRonCloud(rcParam string) error {
	resp, err := public.RongCloudRequest("/group/join.json", rcParam)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	rcReq := &public.RongCloudComResponse{}
	err = json.Unmarshal(body, &rcReq)
	if err != nil {
		return err
	}
	if rcReq.Code != 200 {
		reason := fmt.Sprintf("rongcloud error code is %d errorMsg is %s", rcReq.Code, rcReq.ErrorMessage)
		return errors.New(reason)
	}
	return nil
}

// 调用融云退出群聊接口
func quitGroupRonCloud(rcParam string) error {
	resp, err := public.RongCloudRequest("/group/quit.json", rcParam)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	rcReq := &public.RongCloudComResponse{}
	err = json.Unmarshal(body, &rcReq)
	if err != nil {
		return err
	}
	if rcReq.Code != 200 {
		reason := fmt.Sprintf("rongcloud error code is %d errorMsg is %s", rcReq.Code, rcReq.ErrorMessage)
		return errors.New(reason)
	}
	return err
}

// 发送时间块
func sendTimeBlock(router *gin.Context, groupId string) error {
	groupRobot, err := groupService.GroupRobot(router, BusinessDB, groupId)
	if err != nil {
		return err
	}
	links := make([]map[string]string, 0)
	fromUserId := initParam.RobotTarget + strconv.Itoa(groupRobot.ID)
	toGroupId := groupId
	objectName := initParam.ChatMsgInfo
	msgContent := time.Now().Format(initParam.TimeFormatHourMinute)
	members := dto.MsgMembers{
		UserName:   groupRobot.RobotNickname,
		UserAvatar: groupRobot.RobotImg,
	}
	content := dto.Msg{
		Content:    msgContent,
		Links:      links,
		ObjectName: objectName,
		ChatType:   "group",
		Members:    members,
	}
	contentStr, err := json.Marshal(content)
	if err != nil {
		return err
	}
	rcParam := fmt.Sprintf("fromUserId=%s&toGroupId=%s&objectName=%s&content=%s&isIncludeSender=1", fromUserId, toGroupId, objectName, string(contentStr))
	err = sendRonCloud(rcParam)
	if err != nil {
		return err
	}
	return nil
}

// 发送消息
func synchronizeMessages(queryParamSendMsg dto.ChatMessage) error {
	paramJsonBytes, err := json.Marshal(queryParamSendMsg)
	if err != nil {
		return err
	}
	paramJsonStr := string(paramJsonBytes)
	queryUrl := initParam.BaseConf.Host + "/syncmessage"
	_, queryByte, err := lib.HttpJSON(lib.NewTrace(), queryUrl, paramJsonStr, 60*1000, nil)
	if err != nil {
		return err
	}
	resp := &middleware.Response{}
	if err := json.Unmarshal(queryByte, resp); err != nil {
		return errors.New("joinGroup.sendwelcomemsg resp Unmarshal failed " + err.Error())
	}
	if resp.ErrorCode != middleware.SUCCESS_CODE {
		return errors.New(resp.ErrorMsg)
	}
	return nil
}

// 发送消息
func send(queryParamSendMsg dto.Group) error {
	paramJsonBytes, err := json.Marshal(queryParamSendMsg)
	if err != nil {
		return err
	}
	paramJsonStr := string(paramJsonBytes)
	queryUrl := initParam.BaseConf.Host + "/sendwelcomemsg"
	_, queryByte, err := lib.HttpJSON(lib.NewTrace(), queryUrl, paramJsonStr, 60*1000, nil)
	if err != nil {
		return err
	}
	resp := &middleware.Response{}
	if err := json.Unmarshal(queryByte, resp); err != nil {
		return err
	}
	if resp.ErrorCode != middleware.SUCCESS_CODE {
		return errors.New(resp.ErrorMsg)
	}
	return nil
}

// 发送推送消息
/*func sendNoticeMsg(router *gin.Context, queryParamSendMsg dto.User) error {
	paramJsonBytes, err := json.Marshal(queryParamSendMsg)
	if err != nil {
		return err
	}
	paramJsonStr := string(paramJsonBytes)
	queryUrl := initParam.BaseConf.Host + "/sendnoticemsg"
	_, queryByte, err := lib.HttpJSON(lib.NewTrace(), queryUrl, paramJsonStr, 60*1000, nil)
	if err != nil {
		return err
	}
	resp := &middleware.Response{}
	if err := json.Unmarshal(queryByte, resp); err != nil {
		return err
	}
	if resp.ErrorCode != middleware.SUCCESS_CODE {
		public.ContextNotice(router, "debug", map[string]interface{}{
			"title": router.Request.URL,
			"msg":   fmt.Sprintf("queryParamSendMsg ==> %v", string(paramJsonBytes)),
		})
		return errors.New(resp.ErrorMsg)
	}
	return nil
}*/

// 缓存群数据
func getGroupMsg(router *gin.Context, groupId string) models.WkbGroup {
	cache := cacheMap.Group.Get(groupId)
	if cache == nil {
		group, err := groupService.Group(router, InstanceDB, groupId)
		if err != nil {
			public.ContextNotice(router, "error", map[string]interface{}{
				"title": router.Request.URL,
				"err":   err,
			})
			return group
		}
		cacheMap.Group.Set(groupId, group)
		return group
	}
	cacheMsg := cache.(models.WkbGroup)
	return cacheMsg
}

// 缓存群成员数据
func getGroupMember(router *gin.Context, groupId string) []models.WkbGroupMember {
	cache := cacheMap.GroupMember.Get(groupId)
	if cache == nil {
		cacheMsg := make([]models.WkbGroupMember, 0)
		groupMember, err := groupService.GroupMember(router, InstanceDB, groupId)
		if err != nil {
			public.ContextNotice(router, "error", map[string]interface{}{
				"title": router.Request.URL,
				"err":   err,
			})
			return nil
		}
		for _, v := range groupMember {
			cacheMsg = append(cacheMsg, v)
		}
		cacheMap.GroupMember.Set(groupId, cacheMsg)
		return cacheMsg
	}
	cacheMsg := cache.([]models.WkbGroupMember)
	return cacheMsg
}

// 缓存群成员数据
func updateGroupMember(router *gin.Context, groupId string) error {
	cacheMsg := make([]models.WkbGroupMember, 0)
	groupMember, err := groupService.GroupMember(router, InstanceDB, groupId)
	if err != nil {
		return err
	}
	for _, v := range groupMember {
		cacheMsg = append(cacheMsg, v)
	}
	cacheMap.GroupMember.Set(groupId, cacheMsg)
	return nil
}

// 拉入成员动作 joinType 1 只拉人入群；2 拉人并且把之前的管理和客服踢出；
func join(router *gin.Context, groupId string, groupName string, userDetail dto.UserDetail, skillGroupId int, joinType int) error {
	//判断是否已经在群聊
	groupMemberCs, err := groupService.GroupMemberCs(router, BusinessDB, groupId)
	if err != nil {
		return err
	}
	arr := make([]string, 0)
	for _, v := range groupMemberCs {
		arr = append(arr, gconv.String(v.UserID))
	}
	if gstr.InArray(arr, gconv.String(userDetail.UserId)) {
		return nil
	}
	transaction := BusinessDB.Begin()
	defer func() {
		if err := recover(); err != nil {
			Rollback(router.Request.URL.Path, transaction, errors.New(gconv.String(err)))
		}
	}()
	memberType := 0
	if joinType == 2 {
		memberType = 2
	} else {
		if userDetail.IsManage == 1 {
			memberType = 3
		} else {
			memberType = 4
		}
	}
	groupMember := &models.WkbGroupMember{
		GroupID:    groupId,
		UserID:     userDetail.UserId,
		MemberName: userDetail.Nickname,
		MemberType: memberType,
		Avatar:     userDetail.Avatar,
	}
	err = groupService.CreateGroupMember(router, transaction, groupMember)
	if err != nil {
		Rollback(router.Request.URL.Path, transaction, err)
		return err
	}
	//接待数加1
	err = customerServiceService.UpdateCsUserReceptionNum(router, transaction, userDetail.UserId, skillGroupId)
	if err != nil {
		Rollback(router.Request.URL.Path, transaction, err)
		return err
	}
	//joinGroupUsers := fmt.Sprintf("userId=%d", userDetail.UserId)
	joinGroupUsers := fmt.Sprintf("userId=%s", initParam.CsTarget+gconv.String(userDetail.UserId))
	rcParam := fmt.Sprintf("%s&groupId=%s&groupName=%s", joinGroupUsers, groupId, groupName)
	err = joinGroupRonCloud(rcParam)
	if err != nil {
		RollbackOtherTitle(router.Request.URL.Path, "【RON CLOUD REQUEST ERROR】", transaction, err)
		return err
	}
	transaction.Commit()
	return nil
}

// 查看客服双方是否有聊天，没有则新建一个聊天
func makePrivateGroupBeforeTalk(router *gin.Context, userId string, otherUserId string) (string, error) {
	queryParam := dto.Group{
		UserId:      userId,
		OtherUserId: otherUserId,
	}
	paramJsonBytes, err := json.Marshal(queryParam)
	if err != nil {
		return "", err
	}
	paramJsonStr := string(paramJsonBytes)
	queryUrl := initParam.BaseConf.Host + "/createcsgroup"
	_, queryByte, err := lib.HttpJSON(lib.NewTrace(), queryUrl, paramJsonStr, 60*1000, nil)
	if err != nil {
		return "", err
	}
	resp := &middleware.Response{}
	if err := json.Unmarshal(queryByte, resp); err != nil {
		return "", err
	}
	if resp.ErrorCode != middleware.SUCCESS_CODE {
		return "", err
	}
	//
	groupCs, err := groupService.GroupCs(router, BusinessDB, userId, otherUserId)
	if err != nil {
		return "", err
	}
	if groupCs.GroupID == "" {
		return "", err
	}
	return groupCs.GroupID, nil
}

// 拉人入群 joinType 1 只拉人入群；2 拉人并且把之前的管理和客服踢出；skillGroupId 0 表示不能确定是那个技能组；isSendType 是否发送欢迎语 0 不发送，1 发送
func joinGroup(router *gin.Context, joinType int, isSendType int, userDetail dto.UserDetail, groupMsg models.WkbGroup, skillGroupId int) error {
	//踢人出群
	//踢出群内的客服
	if joinType == 2 {
		groupMemberCs, err := groupService.GroupMemberCs(router, BusinessDB, groupMsg.GroupID)
		if err != nil {
			return err
		}
		userId := ""
		for _, v := range groupMemberCs {
			userId = userId + gconv.String(v.UserID) + ","
		}
		if userId != "" {
			userId = userId[0 : len(userId)-1]
			queryParam := dto.Group{
				GroupId:  groupMsg.GroupID,
				UserId:   userId,
				QuitType: 3,
			}
			err = quitGroup(queryParam)
			if err != nil {
				return err
			}
		}
	}
	//加入群聊
	err := join(router, groupMsg.GroupID, groupMsg.Name, userDetail, skillGroupId, joinType)
	if err != nil {
		return err
	}
	//更新群成员缓存
	err = updateGroupMember(router, groupMsg.GroupID)
	if err != nil {
		return err
	}
	//发送入群提示语
	if isSendType == 1 {
		err = send(dto.Group{
			GroupId: groupMsg.GroupID,
			Msg:     userDetail.Nickname,
			MsgType: 4, //
		})
		if err != nil {
			return err
		}
	}
	//群接入客服（管理员被拉入，转接，邀请），清除当前被拉入的客服的聊天列表缓存
	//群接入客服（管理员被拉入，转接，邀请），清除当前群成员的缓存
	if joinType == 1 {
		updateMessagesListCache(userDetail.UserId)
	}

	//
	updateGroupMemberCache()

	// 系统判定拉入客服，记录到 redis 中
	if joinType == 2 && isSendType == 1 {
		// Channel 属于哪个渠道：1 pc；2 小程序
		channelType := redisModules.RedisChannelTypeUserId("ChannelType_"+gconv.String(groupMsg.UserId), 0)
		ChannelStr := "H5"
		if channelType == 1 {
			ChannelStr = "WEB"
		}
		reportContent(groupMsg.GroupID, dto.WkbImSessionReport{
			CustomerUid:         groupMsg.UserId,
			CustomerNickname:    groupMsg.Name,
			CustomerServiceUid:  userDetail.UserId,
			CustomerServiceName: userDetail.Nickname,
			Channel:             ChannelStr,
			VisitStart:          time.Now().Format(initParam.TimeFormatYear),
			VisitEnd:            "",
			Seconds:             0,
			RecordTime:          time.Now().Unix(),
		})
	}

	return nil
}

// 解散群聊
func modifyGroupState(queryParam dto.Group) error {
	paramJsonBytes, err := json.Marshal(queryParam)
	if err != nil {
		return err
	}
	paramJsonStr := string(paramJsonBytes)
	queryUrl := initParam.BaseConf.Host + "/mgroupstate"
	_, queryByte, err := lib.HttpJSON(lib.NewTrace(), queryUrl, paramJsonStr, 60*1000, nil)
	if err != nil {
		return err
	}
	resp := &middleware.Response{}
	if err := json.Unmarshal(queryByte, resp); err != nil {
		return err
	}
	if resp.ErrorCode != middleware.SUCCESS_CODE {
		return errors.New(resp.ErrorMsg)
	}
	return nil
}

// 踢出群聊
func quitGroup(queryParam dto.Group) error {
	paramJsonBytes, err := json.Marshal(queryParam)
	if err != nil {
		return err
	}
	paramJsonStr := string(paramJsonBytes)
	queryUrl := initParam.BaseConf.Host + "/quitgroup"
	_, queryByte, err := lib.HttpJSON(lib.NewTrace(), queryUrl, paramJsonStr, 60*1000, nil)
	if err != nil {
		return err
	}
	resp := &middleware.Response{}
	if err := json.Unmarshal(queryByte, resp); err != nil {
		return err
	}
	if resp.ErrorCode != middleware.SUCCESS_CODE {
		return errors.New(resp.ErrorMsg)
	}
	return nil
}

// 确认转接和邀请 msgType 1 转接、2 邀请；answer 1 同意、2 拒绝；groupId 私聊的群id；extraGroupId 邀请的源群，转接的源群
func confirmChatMessage(router *gin.Context, msgType int, msgUid string, userId int, answer int, extraGroupId string) error {
	msg := ""
	status := 1
	if answer == 1 {
		status = 2
	} else {
		status = 3
	}
	err := messageService.UpdateMessageStatus(router, BusinessDB, msgUid, extraGroupId, status)
	if err != nil {
		return err
	}
	//获取源群的客户信息
	groupMemberC, err := groupService.GroupMemberC(router, BusinessDB, extraGroupId)
	if err != nil {
		return err
	}
	groupMemberAdmin, err := groupService.GroupMemberAdmin(router, BusinessDB, extraGroupId)
	if err != nil {
		return err
	}
	//UserId 原群的用户id
	//OtherUserId 接受转接的客服id
	if msgType == 1 {
		if answer == 1 {
			msg = "question_accept_do"
		} else {
			msg = "question_refuse_do"
		}
		err = send(dto.Group{
			GroupId:     extraGroupId,
			UserId:      gconv.String(groupMemberC.UserID),
			OtherUserId: gconv.String(userId),
			Extra:       gconv.String(groupMemberAdmin.UserID),
			Msg:         msg,
			MsgType:     11, //
		})
		if err != nil {
			return err
		}
		//taskMap.M.Remove("question_" + groupId + gconv.String(userId))
		taskMap.M.Remove("question_" + extraGroupId + gconv.String(userId))
	} else {
		if answer == 1 {
			msg = "invitation_accept_do"
		} else {
			msg = "invitation_refuse_do"
		}
		err = send(dto.Group{
			GroupId:     extraGroupId,
			UserId:      gconv.String(groupMemberC.UserID),
			OtherUserId: gconv.String(userId),
			Extra:       gconv.String(groupMemberAdmin.UserID),
			Msg:         msg,
			MsgType:     15, //
		})
		if err != nil {
			return err
		}
		//taskMap.M.Remove("invitation_" + groupId + gconv.String(userId))
		taskMap.M.Remove("invitation_" + extraGroupId + gconv.String(userId))
	}
	return nil
}

// 本地时区定义
func parseWithLocation(name string, timeStr string) (time.Time, error) {
	locationName := name
	if l, err := time.LoadLocation(locationName); err != nil {
		println(err.Error())
		return time.Time{}, err
	} else {
		lt, err := time.ParseInLocation(initParam.TimeFormatYear, timeStr, l)
		if err != nil {
			return lt, err
		}
		fmt.Println(locationName, lt)
		return lt, nil
	}
}

// 清除群成员缓存
func clearGroupMemberCache(groupId string, cacheQuitUserId []string) {
	cache := cacheMap.GroupMember.Get(groupId)
	if cache != nil {
		newCache := make([]models.WkbGroupMember, 0)
		cacheGroupMember := cache.([]models.WkbGroupMember)
		for _, v := range cacheGroupMember {
			if v.MemberType <= 1 || gstr.InArray(cacheQuitUserId, gconv.String(v.UserID)) {
				newCache = append(newCache, v)
			}
		}
		cacheMap.GroupMember.Set(groupId, newCache)
	}
}

//
func getWxToken() (string, error) {
	urls := fmt.Sprintf(`http://www.emakerzone.com/wx_token.json`)
	resp, err := http.Get(urls)
	if err != nil {
		return "", err
	}
	bytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}
	token := string(bytes)
	if token == "" {
		return "", errors.New("token is exist")
	}
	return token, nil
}

// 发送公众号模板消息
func sendOfficialAccountMsg(isCustomerClient int, toUser string, gotoMini int) error {
	token, err := getWxToken()
	if err != nil {
		return err
	}
	appid := ""
	path := ""
	first := initParam.UnreadMessageText
	//消息提醒
	keyword3 := ""
	if isCustomerClient == 1 {
		if gotoMini == 1 {
			appid = initParam.WeChatFAEConf.AppId
			path = "pages/login/login"
		}
		keyword3 = initParam.UnreadCsHintText
	} else {
		if gotoMini == 1 {
			appid = initParam.WeChatClientConf.AppId
			path = "pages/login/login"
		}
		keyword3 = initParam.UnreadCHintText
	}
	keyword2 := time.Now().Format(initParam.TimeFormatYear)
	remark := initParam.UnreadClickHintText
	urls := fmt.Sprintf(`https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=%s`, token)
	raw := `{
			"touser":"%s",
			"template_id":"%s",
			"url":"",  
			"miniprogram":{
				"appid": "%s",
    			"path": "%s"
			},          
			"data":{
				"first": {
					"value":"%s",
					"color":"#173177"
				},
				"keyword1":{
					"value":"%s",
					"color":"#173177"
				},
				"keyword2": {
					"value":"%s",
					"color":"#173177"
				},
				"keyword3": {
					"value":"%s",
					"color":"#173177"
				},
				"remark": {
					"value":"%s",
					"color":"#173177"
				}
			}
		}`
	reader := strings.NewReader(fmt.Sprintf(raw, toUser, initParam.GZHTemplateId, appid, path, first, initParam.ProductNamePlatForm, keyword2, keyword3, remark))
	resp, err := http.Post(urls, "application/json", reader)
	if err != nil {
		return err
	}
	bytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	fmt.Println(string(bytes))
	return nil
}

func weChatLoginQrCode() string {
	qrCode := ""
	appId := "wxc2e336bd0dab43ae"
	redirectUri := url.QueryEscape("http://www.emakerzone.com/EZS/cs.html")
	urls := fmt.Sprintf(`https://open.weixin.qq.com/connect/qrconnect?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_login&state=STATE#wechat_redirect`, appId, redirectUri)
	fmt.Println(urls)
	resp, err := http.Get(urls)
	if err != nil {
		return qrCode
	}
	utf8Reader := transform.NewReader(resp.Body, simplifiedchinese.GBK.NewDecoder())
	bytes, err := ioutil.ReadAll(utf8Reader)
	if err != nil {
		return qrCode
	}
	//fmt.Println(string(bytes))
	xml := string(bytes)
	reg := regexp.MustCompile(`/connect/qrcode/[\w]+`)
	qrCodeArr := reg.FindAllString(xml, -1)
	for _, v := range qrCodeArr {
		qrCode = "https://open.weixin.qq.com" + v
	}
	fmt.Println(qrCode)
	return qrCode
}

// 发送小程序客服消息
func sendMiniProgramMsg(isCustomerClient int, toUser string) error {
	app := redisModules.App{}
	content := ""
	if isCustomerClient == 1 {
		app = redisModules.RedisAppToken(initParam.WeChatFAEConf.AppId, initParam.WeChatFAEConf.Secret, redisModules.App{})
		content = initParam.UnreadMessageText
	} else {
		app = redisModules.RedisAppToken(initParam.WeChatClientConf.AppId, initParam.WeChatClientConf.Secret, redisModules.App{})
		content = initParam.UnreadMessageText
	}
	//urls := fmt.Sprintf(`https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=%s`, app.Token)
	//raw := `{
	//	"touser":"%s",
	//	"msgtype":"text",
	//	"text":
	//	{
	//     	"content":"%s"
	//	}
	//}`
	templateId := "XAohTBh0I6VLcOQuqOThBNlanAVgulQEEwI3ZbJwfPY"
	thing01 := initParam.ProductNamePlatForm
	thing02 := content
	thing03 := time.Now().Format(initParam.TimeFormatYear)
	urls := fmt.Sprintf(`https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=%s`, app.Token)
	raw := `{
  		"touser": "%s",
  		"template_id": "%s",
  		"page": "index",
  		"miniprogram_state":"developer",
  		"lang":"zh_CN",
  		"data": {
			"thing01": {
				"value": "%s"
			},
			"thing02": {
				"value": "%s"
			},
			"thing03": {
				"value": "%s"
			}
  		}
	}`
	reader := strings.NewReader(fmt.Sprintf(raw, toUser, templateId, thing01, thing02, thing03))
	resp, err := http.Post(urls, "application/json", reader)
	if err != nil {
		return err
	}
	bytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	fmt.Println(string(bytes))
	return nil
}

// 发送短信消息
func sendMsg(mobile string, appName string, code string) {
	tplId := "30388"
	codeValue := url.Values{}
	codeValue.Set("#code#", code)
	codeValueEncode := codeValue.Encode()
	appValue := url.Values{}
	appValue.Set("#app#", appName)
	appValueEncode := appValue.Encode()
	key := "2f28a3ea6f9d88026ead345e4953e931"
	msmUrl := fmt.Sprintf("http://v.juhe.cn/sms/send?mobile=%s&tpl_id=%s&tpl_value=%s&%s&key=%s", mobile, tplId, codeValueEncode, appValueEncode, key)
	resp, err := http.Get(msmUrl)
	if err != nil {
		return
	}
	bytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}
	fmt.Println(string(bytes))
}

// 聊天列表
func messagesList(router *gin.Context, params *dto.User) (data []dto.ChatMessagePage, count int, err error) {
	//聊天列表查询
	ChatMessagePage, count, err := messageService.MessagesList(BusinessDB, params.UserId, params.Search, params.PageNum, params.PageSize)
	if err != nil {
		if err.Error() != initParam.DataIsNotExist {
			count = 0
			return
		}
		if params.Search == "" && params.PageNum <= 1 {
			// 查询是否有客服机器人群，没有则创建
			err = checkGroupRACs(router, params.UserId)
			if err != nil {
				count = 0
				return
			}
		}
		count = 0
		return
	}
	if ChatMessagePage == nil || len(ChatMessagePage) == 0 {
		//如果没有对话，则默认新建一条机器人的对话
		if params.Search == "" && params.PageNum <= 1 {
			err = checkGroupRACs(router, params.UserId)
			if err != nil {
				count = 0
				return
			}
		}
		count = 0
		return
	}
	for _, v := range ChatMessagePage {
		channelType := 0
		newLastChat := dto.ChatMessagePage{}
		if v.ChatType == 2 {
			if v.ContentText == "" {
				continue
			}
			if !strings.Contains(v.FromUserId, "robot") && (strings.Contains(v.ContentText, initParam.TransMessageText) || strings.Contains(v.ContentText, initParam.InvitationMessageText)) && v.FromUserId == params.UserId {
				//查询非当前最后一条的最后一条
				groupId := v.GroupId
				value, err := messageService.CsMessageLast(BusinessDB, groupId, params.UserId)
				if err != nil {
					continue
				}
				if value.UserId == "" {
					continue
				}
				v = value
			}
			if gconv.Int(v.OtherUserId) == 0 {
				csMsg := cacheMap.CustomerService.Get(v.OtherUserId)
				if csMsg == nil {
					robot, err := robotService.Robot(router, BusinessDB)
					if err != nil {
						continue
					}
					cacheMap.CustomerService.Set(v.OtherUserId, cacheMap.CustomerServiceMsg{
						Name:   robot.RobotNickname,
						Avatar: robot.RobotImg,
					})
					csMsg = cacheMap.CustomerService.Get(v.OtherUserId)
				}
				v.Name = csMsg.(cacheMap.CustomerServiceMsg).Name
				v.Avatar = csMsg.(cacheMap.CustomerServiceMsg).Avatar
			} else {
				if v.UserId != params.UserId {
					csMsg := cacheMap.CustomerService.Get(v.UserId)
					if csMsg == nil {
						csUser, err := customerServiceService.CsUser(router, BusinessDB, gconv.Int(v.UserId))
						if err != nil {
							continue
						}
						cacheMap.CustomerService.Set(v.UserId, cacheMap.CustomerServiceMsg{
							Name:   csUser.RealName,
							Avatar: initParam.FAEAvatar,
						})
						csMsg = cacheMap.CustomerService.Get(v.UserId)
					}
					v.Name = v.UserName
					v.Avatar = initParam.FAEAvatar
				}
				if v.OtherUserId != params.UserId {
					csMsg := cacheMap.CustomerService.Get(v.OtherUserId)
					if csMsg == nil {
						csUser, err := customerServiceService.CsUser(router, BusinessDB, gconv.Int(v.OtherUserId))
						if err != nil {
							continue
						}
						cacheMap.CustomerService.Set(v.OtherUserId, cacheMap.CustomerServiceMsg{
							Name:   csUser.RealName,
							Avatar: initParam.FAEAvatar,
						})
						csMsg = cacheMap.CustomerService.Get(v.OtherUserId)
					}
					v.Name = v.UserName
					v.Avatar = initParam.FAEAvatar
				}
			}
		} else {
			if v.ObjectName == initParam.ChatMsgInfo {
				//查询非当前最后一条的最后一条
				groupId := v.GroupId
				value, err := messageService.CMessageLast(router, BusinessDB, groupId, params.UserId)
				if err != nil {
					continue
				}
				if value.UserId == "" {
					continue
				}
				v = value
			}
			v.Name = v.GroupName
			channelType = redisModules.RedisChannelTypeUserId("ChannelType_"+v.UserId, 0)
		}
		vv := &dto.Msg{}
		contentText := ""
		if newLastChat.FromUserId == "" {
			contentText = v.ContentText
			_ = json.Unmarshal([]byte(v.ContentText), vv)
		} else {
			contentText = newLastChat.ContentText
			_ = json.Unmarshal([]byte(newLastChat.ContentText), vv)
		}
		contentStruct := dto.Msg{
			MessageType: v.ObjectName,
			Content:     vv.Content,
			Links:       vv.Links,
			Members:     vv.Members,
			Style:       vv.Style,
			Extra:       vv.Extra,
			ObjectName:  v.ObjectName,
			ChatType:    vv.ChatType,
			ImageUri:    vv.ImageUri,
			H:           vv.H,
			W:           vv.W,
			RemoteUrl:   vv.RemoteUrl,
			Duration:    vv.Duration,
			Name:        vv.Name,
			FileUrl:     vv.FileUrl,
			Size:        vv.Size,
			Type:        vv.Type,
		}
		data = append(data, dto.ChatMessagePage{
			GroupId:       v.GroupId,
			Name:          v.Name,
			Avatar:        v.Avatar,
			ContentText:   contentText,
			ContentStruct: contentStruct,
			CreateTime:    v.CreateTime,
			State:         v.State,
			ChannelType:   channelType,
			NoReadNum:     0,
			ChatType:      v.ChatType,
			MemberType:    v.MemberType,
		})
	}
	return
}

// TODO 更新消息列表
func updateMessagesListCache(userId int) {
	fmt.Println(userId)
}

// TODO 更新群成员
func updateGroupMemberCache() {

}

// TODO 更新通讯录
func updateAddrBookCache() {

}

// TODO 更新 redis
func updateRedis(router *gin.Context, groupId string) {
	groupMemberCs, err := groupService.GroupMemberCs(router, BusinessDB, groupId)
	if err != nil {
		return
	}
	// 更新聊天列表
	for _, v := range groupMemberCs {
		updateMessagesListCache(v.UserID)
	}
	// 更新群成员
	updateGroupMemberCache()
	// 更新通讯录
	updateAddrBookCache()
}

// 清除邀请，转接申请
func DeleteHeap(heapBaseName string) {
	deleteKey := make([]string, 0)
	keyStrArr := make([]string, 0)
	taskMap.M.Iterator(func(key, value interface{}) bool {
		valueSendMap := value.(taskMap.SendMap)
		keyStr := key.(string)
		keyStrArr = append(keyStrArr, keyStr)
		if (valueSendMap.Type.Val() == "invitation" || valueSendMap.Type.Val() == "question") && gstr.Contains(keyStr, heapBaseName) {
			deleteKey = append(deleteKey, key.(string))
		}
		return true
	})
	if len(deleteKey) > 0 {
		for _, v := range deleteKey {
			taskMap.M.Remove(v)
		}
	}
}

// 添加缓存
func addRedis(router *gin.Context, data interface{}, redisKey string) error {
	jsonContent, err := json.Marshal(data)
	if err != nil {
		public.ContextNotice(router, "error", map[string]interface{}{
			"title": router.Request.URL,
			"err":   err,
		})
		return err
	}
	result := redisModules.ClientRedis.Set(redisKey, jsonContent, 300*time.Second)
	if result.Err() != nil {
		return result.Err()
	}
	return nil
}

// 添加缓存
/*func addListRedis(router *gin.Context, data interface{}, redisKey string) {
	jsonContent, err := json.Marshal(data)
	if err != nil {
		public.ContextNotice(router, "error", map[string]interface{}{
			"title": router.Request.URL,
			"err":   err,
		})
		return
	}
	redisModules.ClientRedis.LPush(redisKey, jsonContent)
}*/

// 生成小程序外链
func generateUrlLink(router *gin.Context, isCustomerClient int, path string, query string, weChatData *dto.WechatLoginOutput) error {
	app := redisModules.App{}
	if isCustomerClient == 1 {
		app = redisModules.RedisAppToken(initParam.WeChatClientConf.AppId, initParam.WeChatClientConf.Secret, redisModules.App{})
	} else {
		app = redisModules.RedisAppToken(initParam.WeChatFAEConf.AppId, initParam.WeChatFAEConf.Secret, redisModules.App{})
	}
	//urls := fmt.Sprintf(`https://api.weixin.qq.com/wxa/generate_urllink?access_token=%s`, app.Token)
	//{
	//    "path": "/pages/publishHomework/publishHomework",
	//    "query": "",
	//    "is_expire":true,
	//    "expire_type":1,
	//    "expire_interval":1,
	//    "cloud_base":
	//    {
	//        "env": "xxx",
	//        "domain": "xxx.xx",
	//        "path": "/jump-wxa.html",
	//        "query": "a=1&b=2"
	//    }
	//}
	urls := fmt.Sprintf(`https://api.weixin.qq.com/wxa/generate_urllink?access_token=%s`, app.Token)
	raw := `{
  		"path": "%s",
  		"query": "%s",
  		"is_expire":false
	}`
	reader := strings.NewReader(fmt.Sprintf(raw, path, query))
	resp, err := http.Post(urls, "application/json", reader)
	if err != nil {
		return err
	}
	bytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	//{"errcode":85079,"errmsg":"miniprogram has no online release rid: 60bdd38f-234bf3fb-28060ad4"}
	req := &dto.WechatLoginOutput{}
	err = json.Unmarshal(bytes, &req)
	if err != nil {
		return err
	}
	if req.Errcode != 0 {
		errorMsg := req.Errmsg
		if req.Errcode == 85079 {
			errorMsg = "小程序 还未发布！！"
		}
		public.ContextNotice(router, "error", map[string]interface{}{
			"title":    "generateUrlLink",
			"urls":     urls,
			"reader":   reader,
			"req":      req,
			"errorMsg": errorMsg,
		})
		return errors.New(errorMsg)
	}
	weChatData.UrlLink = req.UrlLink
	return nil
}

// 创建客服助手群事务
func robotMsgTransaction(router *gin.Context, groupId string, robot dto.Robot, user dto.UserDetail) error {
	transaction := InstanceDB.Begin()
	defer func() {
		if err := recover(); err != nil {
			Rollback(router.Request.URL.Path, transaction, errors.New(gconv.String(err)))
		}
	}()
	robotId := gconv.String(robot.Id)
	groupName := robot.RobotNickname
	group := &models.WkbGroup{
		GroupID:      groupId,
		Name:         robot.RobotNickname,
		GroupType:    2,
		State:        1,
		UserId:       user.UserId,
		OtherUserId:  0,
		LastChatId:   0,
		LastChatTime: time.Now(),
	}
	err := groupService.CreateGroup(router, transaction, group)
	if err != nil {
		Rollback(router.Request.URL.Path, transaction, err)
		return err
	}
	//
	robotGroupMeMember := &models.WkbGroupMember{
		GroupID:    groupId,
		MemberName: robot.RobotNickname,
		MemberType: int(models.ROBOT),
		Avatar:     robot.RobotImg,
		UserID:     robot.Id,
	}
	err = groupService.CreateGroupMember(router, transaction, robotGroupMeMember)
	if err != nil {
		Rollback(router.Request.URL.Path, transaction, err)
		return err
	}
	findUserMemberType := 4
	userGroupMember := &models.WkbGroupMember{
		GroupID:    groupId,
		MemberName: user.Nickname,
		MemberType: findUserMemberType,
		Avatar:     user.Avatar,
		UserID:     user.UserId,
	}
	err = groupService.CreateGroupMember(router, transaction, userGroupMember)
	if err != nil {
		Rollback(router.Request.URL.Path, transaction, err)
		return err
	}
	//调用融云接口创建群
	userId := initParam.CsTarget + gconv.String(user.UserId)
	rcParam := fmt.Sprintf("userId=%s&userId=%s&groupId=%s&groupName=%s", userId, initParam.RobotTarget+robotId, groupId, groupName)
	err = createGroupRonCloud(rcParam)
	if err != nil {
		RollbackOtherTitle(router.Request.URL.Path, "【RON CLOUD REQUEST ERROR】", transaction, err)
		return err
	}
	transaction.Commit()
	return nil
}

// 退出群聊事务
func quitGroupTransaction(router *gin.Context, params *dto.Group, groupMembers []models.WkbGroupMember, canQuitUserId []string, quitUserId string, cacheQuitUserId []string, quitMemberName string) error {
	transaction := BusinessDB.Begin()
	defer func() {
		if err := recover(); err != nil {
			Rollback(router.Request.URL.Path, transaction, errors.New(gconv.String(err)))
		}
	}()
	if strings.Contains(params.UserId, ",") {
		userIds := strings.Split(params.UserId, ",")
		for _, id := range userIds {
			if !gstr.InArray(canQuitUserId, id) {
				continue
			}
			if len(quitUserId) > 0 {
				quitUserId = quitUserId + "&"
			}
			//quitUserId = quitUserId + "userId=" + id
			quitUserId = quitUserId + "userId=" + initParam.CsTarget + id
			err := groupService.DeleteGroupMember(router, transaction, params.GroupId, id)
			if err != nil {
				Rollback(router.Request.URL.Path, transaction, err)
				return err
			}
			cacheQuitUserId = append(cacheQuitUserId, id)
			for _, member := range groupMembers {
				if id == gconv.String(member.UserID) {
					quitMemberName = quitMemberName + member.MemberName + ","
				}
			}
		}
	} else {
		if !gstr.InArray(canQuitUserId, params.UserId) {
			err := groupService.UpdateGroupState(router, transaction, params.GroupId, 2)
			if err != nil {
				Rollback(router.Request.URL.Path, transaction, err)
				return err
			}
			transaction.Commit()
			return errors.New("LOGICAL SUCCESS")
		}
		//quitUserId = "userId=" + params.UserId
		quitUserId = "userId=" + initParam.CsTarget + params.UserId
		err := groupService.DeleteGroupMember(router, transaction, params.GroupId, params.UserId)
		if err != nil {
			Rollback(router.Request.URL.Path, transaction, err)
			return err
		}
		cacheQuitUserId = append(cacheQuitUserId, params.UserId)
		for _, member := range groupMembers {
			if params.UserId == gconv.String(member.UserID) {
				quitMemberName = quitMemberName + member.MemberName + ","
			}
		}
	}
	transaction.Commit()
	return nil
}

// 创建客服私聊事务
func createCsGroupTransaction(router *gin.Context, params *dto.Group, groupId string, findOtherUser dto.UserDetail, findUser dto.UserDetail) (*dto.Groups, error) {
	// 用户之前没有群组
	transaction := BusinessDB.Begin()
	defer func() {
		if err := recover(); err != nil {
			Rollback(router.Request.URL.Path, transaction, errors.New(gconv.String(err)))
		}
	}()
	groupId = public.GenID()
	//创建群
	userId, err := strconv.Atoi(params.UserId)
	if err != nil {
		return nil, err
	}
	otherUserId, err := strconv.Atoi(params.OtherUserId)
	if err != nil {
		return nil, err
	}
	group := &models.WkbGroup{
		GroupID:      groupId,
		Name:         params.GroupName,
		GroupType:    2,
		State:        1,
		UserId:       userId,
		OtherUserId:  otherUserId,
		LastChatId:   0,
		LastChatTime: time.Now(),
	}
	err = groupService.CreateGroup(router, transaction, group)
	if err != nil {
		Rollback(router.Request.URL.Path, transaction, err)
		return nil, err
	}
	// MemberType 成员类型,0用户,1机器人,2管理员,3客服,4FAE
	//将对方拉入私聊
	//管理员 2，客服 3，fae 4
	//FindUserByUserId im_user_type 1=普通用户,2=客服,3=客服（管理员）
	findOtherUserMemberType := 0
	switch findOtherUser.ImUserType {
	case 1:
		break
	case 2:
		findOtherUserMemberType = 3
		break
	case 3:
		findOtherUserMemberType = 2
		break
	}
	otherGroupMeMember := &models.WkbGroupMember{
		GroupID:    groupId,
		MemberName: findOtherUser.Nickname,
		MemberType: findOtherUserMemberType,
		Avatar:     findOtherUser.Avatar,
		UserID:     findOtherUser.UserId,
	}
	otherGroupMeMemberDto := &dto.GroupMembers{
		UserName: findOtherUser.Nickname,
		Avatar:   findOtherUser.Avatar,
		UserType: findOtherUser.ImUserType,
	}
	err = groupService.CreateGroupMember(router, transaction, otherGroupMeMember)
	if err != nil {
		Rollback(router.Request.URL.Path, transaction, err)
		return nil, err
	}
	findUserMemberType := 0
	switch findUser.ImUserType {
	case 1:
		break
	case 2:
		findUserMemberType = 3
		break
	case 3:
		findUserMemberType = 2
		break
	}
	userGroupMember := &models.WkbGroupMember{
		GroupID:    groupId,
		MemberName: findUser.Nickname,
		MemberType: findUserMemberType,
		Avatar:     findUser.Avatar,
		UserID:     findUser.UserId,
	}
	userGroupMemberDto := &dto.GroupMembers{
		UserName: findUser.Nickname,
		Avatar:   findUser.Avatar,
		UserType: findUser.ImUserType,
	}
	err = groupService.CreateGroupMember(router, transaction, userGroupMember)
	if err != nil {
		Rollback(router.Request.URL.Path, transaction, err)
		return nil, err
	}
	//构造返回数据
	reMembers := make([]dto.GroupMembers, 0)
	reMembers = append(reMembers, *otherGroupMeMemberDto)
	reMembers = append(reMembers, *userGroupMemberDto)
	reData := &dto.Groups{
		GroupId:      groupId,
		GroupName:    params.GroupName,
		GroupMembers: reMembers,
	}
	//调用融云接口创建群
	params.UserId = initParam.CsTarget + params.UserId
	params.OtherUserId = initParam.CsTarget + params.OtherUserId
	rcParam := fmt.Sprintf("userId=%s&userId=%s&groupId=%s&groupName=%s", params.UserId, params.OtherUserId, groupId, params.GroupName)
	err = createGroupRonCloud(rcParam)
	if err != nil {
		RollbackOtherTitle(router.Request.URL.Path, "【RON CLOUD REQUEST ERROR】", transaction, err)
		return nil, err
	}
	transaction.Commit()
	return reData, nil
}

// 创建客户群聊事务
func createGroupTransaction(router *gin.Context, params *dto.Group, groupId string, findUser dto.UserDetail) (*dto.Groups, error) {
	// 用户之前没有群组
	transaction := BusinessDB.Begin()
	groupId = public.GenID()
	groupNameNew := params.GroupName
	if groupNameNew == "" {
		groupNameNew = findUser.Nickname
	}
	if groupNameNew == "" {
		groupNameNew = findUser.WxNickname
	}
	if groupNameNew == "" {
		groupNameNew = "用户ID" + params.UserId
	}
	//创建群
	group := &models.WkbGroup{
		GroupID:      groupId,
		Name:         groupNameNew,
		GroupType:    1,
		State:        1,
		UserId:       gconv.Int(params.UserId),
		OtherUserId:  0,
		LastChatId:   0,
		LastChatTime: time.Now(),
	}
	err := groupService.CreateGroup(router, transaction, group)
	if err != nil {
		Rollback(router.Request.URL.Path, transaction, err)
		return nil, err
	}
	//群数据写入缓存
	cacheMap.Group.Set(groupId, models.WkbGroup{
		GroupID:   groupId,
		Name:      groupNameNew,
		GroupType: 1,
		State:     1,
		UserId:    gconv.Int(params.UserId),
	})
	//用户类型:1=普通用户,2=客服
	// MemberType 成员类型,0用户,1机器人,2管理员,3客服,4FAE
	memberType := int(models.USER)
	//将机器人拉入群聊
	robot, err := robotService.Robot(router, transaction)
	if err != nil {
		Rollback(router.Request.URL.Path, transaction, err)
		return nil, err
	}
	robotImg := robot.RobotImg
	robotGroupMeMember := &models.WkbGroupMember{
		GroupID:    groupId,
		MemberName: robot.RobotNickname,
		MemberType: int(models.ROBOT),
		Avatar:     robotImg,
		UserID:     robot.Id,
	}
	robotGroupMeMemberDto := &dto.GroupMembers{
		UserName: robot.RobotNickname,
		Avatar:   robotImg,
		UserType: int(models.ROBOT),
	}
	err = groupService.CreateGroupMember(router, transaction, robotGroupMeMember)
	if err != nil {
		Rollback(router.Request.URL.Path, transaction, err)
		return nil, err
	}
	if !strings.Contains(findUser.Avatar, "http") {
		findUser.Avatar = initParam.CAvatar
	}
	if findUser.Nickname == "" {

	}
	userGroupMember := &models.WkbGroupMember{
		GroupID:    groupId,
		MemberName: findUser.Nickname,
		MemberType: memberType,
		Avatar:     findUser.Avatar,
		UserID:     findUser.UserId,
	}
	userGroupMemberDto := &dto.GroupMembers{
		UserName: findUser.Nickname,
		Avatar:   findUser.Avatar,
		UserType: memberType,
	}
	err = groupService.CreateGroupMember(router, transaction, userGroupMember)
	if err != nil {
		Rollback(router.Request.URL.Path, transaction, err)
		return nil, err
	}
	//构造返回数据
	reMembers := make([]dto.GroupMembers, 0)
	reMembers = append(reMembers, *robotGroupMeMemberDto)
	reMembers = append(reMembers, *userGroupMemberDto)
	reData := &dto.Groups{
		GroupId:      groupId,
		GroupName:    groupNameNew,
		GroupMembers: reMembers,
	}
	//群成员数据写入缓存
	groupMemberCache := make([]models.WkbGroupMember, 0)
	groupMemberCache = append(groupMemberCache, models.WkbGroupMember{
		MemberName: robot.RobotNickname,
		Avatar:     robotImg,
		MemberType: int(models.ROBOT),
	})
	groupMemberCache = append(groupMemberCache, models.WkbGroupMember{
		MemberName: findUser.Nickname,
		Avatar:     findUser.Avatar,
		MemberType: memberType,
	})
	cacheMap.GroupMember.Set(groupId, groupMemberCache)
	//调用融云接口创建群
	robotId := strconv.Itoa(robotGroupMeMember.UserID)
	params.UserId = initParam.CTarget + params.UserId
	rcParam := fmt.Sprintf("userId=%s&userId=%s&groupId=%s&groupName=%s", params.UserId, initParam.RobotTarget+robotId, groupId, groupNameNew)
	err = createGroupRonCloud(rcParam)
	if err != nil {
		RollbackOtherTitle(router.Request.URL.Path, "【RON CLOUD REQUEST ERROR】", transaction, err)
		return nil, err
	}
	transaction.Commit()
	return reData, nil
}

// 查询 融云 群是否在数据库记录
func rcGroupInDb(router *gin.Context, params *dto.Group, groupIds []string) ([]dto.GroupRongCloudInfo, error) {
	data := make([]dto.GroupRongCloudInfo, 0)
	if len(groupIds) > 0 {
		groupUser, err := groupService.UserInGroup(router, BusinessDB, groupIds, params.UserId)
		if err != nil {
			return data, err
		}
		if len(groupUser) > 0 {
			//
			groupUser[0].IsBrowseCurrentMsg = redisInt(router, initParam.BrowseCurrentPrefix+params.GroupId, 0)
			data = append(data, groupUser[0])
		}
	} else {
		return data, errors.New(initParam.DataIsNotExist)
	}
	if len(data) == 0 {
		return data, errors.New(initParam.DataIsNotExist)
	}
	return data, nil
}

//
func queryUserJoinGroup(params *dto.Group) (string, error) {
	groupId := ""
	queryParam := dto.Group{
		PreMsg: dto.PreMsg{
			IsCustomerClient: 1,
		},
		UserId: params.UserId,
	}
	paramJsonBytes, err := json.Marshal(queryParam)
	if err != nil {
		return groupId, err
	}
	paramJsonStr := string(paramJsonBytes)
	queryUrl := initParam.BaseConf.Host + "/queryjoingroup"
	_, queryByte, err := lib.HttpJSON(lib.NewTrace(), queryUrl, paramJsonStr, 60*1000, nil)
	if err != nil {
		return groupId, err
	}
	resp := &middleware.Response{}
	if err := json.Unmarshal(queryByte, resp); err != nil {
		return groupId, err
	}
	if resp.ErrorCode != middleware.SUCCESS_CODE {
		return groupId, errors.New(resp.ErrorMsg)
	}
	if resp.Data != nil {
		groups := resp.Data.([]interface{})
		if len(groups) > 0 {
			joinGroup := &dto.GroupRongCloudInfo{}
			if err := mapstructure.Decode(groups[0], joinGroup); err != nil {
				return groupId, err
			}
			groupId = joinGroup.Id
		}
	}
	return groupId, nil
}

//
func queryGroupMember(queryParam dto.Group) (dto.Groups, error) {
	groupMembers := dto.Groups{}
	paramJsonBytes, err := json.Marshal(queryParam)
	if err != nil {
		return groupMembers, err
	}
	paramJsonStr := string(paramJsonBytes)
	queryUrl := initParam.BaseConf.Host + "/querygroupmember"
	_, queryByte, err := lib.HttpJSON(lib.NewTrace(), queryUrl, paramJsonStr, 60*1000, nil)
	if err != nil {
		return groupMembers, err
	}
	resp := &middleware.Response{}
	if err := json.Unmarshal(queryByte, resp); err != nil {
		return groupMembers, err
	}
	if resp.ErrorCode != middleware.SUCCESS_CODE {
		return groupMembers, errors.New(resp.ErrorMsg)
	}
	if err := mapstructure.Decode(resp.Data, &groupMembers); err != nil {
		return groupMembers, err
	}
	return groupMembers, nil
}

//
func addrBookList(router *gin.Context, params *dto.User, csUser dto.UserDetail, csGroupStr string, exceptMember []string) (dto.AddrBookList, error) {
	data := dto.AddrBookList{
		Page: dto.Page{
			PageNum:  params.PageNum,
			PageSize: params.PageSize,
		},
	}
	addrBookList, count, err := customerServiceService.AddrBookList(router, BusinessDB, params.PageNum, params.PageSize,
		params.UserId, params.Search, csUser.SeeCustomerService, csGroupStr, params.AccessType)
	invitationMember := make([]string, 0)
	invitationMemberCache := cacheMap.InvitationMember.Get(params.GroupId)
	if invitationMemberCache != nil {
		invitationMember = invitationMemberCache.([]string)
	}
	if err != nil {
		if err.Error() == sql.ErrNoRows.Error() {
			return data, errors.New(initParam.DataIsNotExist)
		}
		return data, err
	}
	groupIdArr := make([]string, 0)
	groupNameArr := make([]string, 0)
	for _, v := range addrBookList {
		if !lib.InArrayString(v.GroupId, groupIdArr) {
			groupIdArr = append(groupIdArr, v.GroupId)
			groupNameArr = append(groupNameArr, v.GroupName)
		}
	}
	returnMsg := make([]dto.AddrBooks, 0)
	for i := 0; i < len(groupIdArr); i++ {
		abl := dto.AddrBooks{
			GroupId:   groupIdArr[i],
			GroupName: groupNameArr[i],
		}
		allNum := 0
		onlineNum := 0
		for _, v := range addrBookList {
			//
			v.InGroup = 0
			if params.AccessType == 0 && len(invitationMember) > 0 {
				if gstr.InArray(invitationMember, gconv.String(v.UserId)) {
					v.InGroup = 1
				}
			}
			if gstr.InArray(exceptMember, gconv.String(v.UserId)) {
				v.InGroup = 1
			}
			//
			v.Avatar = initParam.FAEAvatar
			//
			if v.GroupId == groupIdArr[i] {
				abl.GroupMember = append(abl.GroupMember, v)
				allNum++
				if v.IsOnline == 1 {
					onlineNum++
				}
			}
		}
		abl.AllNum = allNum
		abl.OnlineNum = onlineNum
		returnMsg = append(returnMsg, abl)
	}
	if returnMsg == nil {
		responseData := dto.NullComeBack{
			Page: dto.Page{
				PageNum:  params.PageNum,
				PageSize: params.PageSize,
				Total:    count,
			},
			List: make([]string, 0),
		}
		middleware.ResponseSuccess(router, responseData)
		return data, errors.New(initParam.DataIsNotExist)
	}
	data.Page.Total = count
	data.AddrBooks = returnMsg
	return data, nil
}

//
func redisInt(router *gin.Context, redisKey string, defaultValue int) int {
	redisCache := redisModules.ClientRedis.Get(redisKey)
	val, err := redisCache.Result()
	result := 0
	if err == nil {
		err = json.Unmarshal([]byte(val), &result)
		if err != nil {
			public.ContextNotice(router, "error", map[string]interface{}{
				"title": router.Request.URL,
				"err":   err,
			})
			return result
		}
	} else {
		if err != redis.Nil {
			public.ContextNotice(router, "error", map[string]interface{}{
				"title": router.Request.URL,
				"err":   err,
			})
			return result
		}
		err = addRedis(router, defaultValue, redisKey)
		if err != nil {
			public.ContextNotice(router, "error", map[string]interface{}{
				"title": router.Request.URL,
				"err":   err,
			})
			return result
		}
	}
	return result
}

// 转接消息处理
func transMessage(msg dto.Msg, groupId string, otherUserId string, msgUId string) {
	if strings.Contains(msg.Content, initParam.TransMessageText) {
		timeout := gconv.String(time.Now().Add(initParam.TransferTimeOut).Unix())
		s := taskMap.SendMap{
			FromUserID:     gtype.NewString(gconv.String(otherUserId)),
			ToUserID:       gtype.NewString(msg.Extra),
			TimeStamp:      gtype.NewInt(gconv.Int(timeout)),
			MsgUid:         gtype.NewString(msgUId),
			Type:           gtype.NewString("question"),
			PrivateGroupId: gtype.NewString(groupId),
		}
		//私聊群id+对方user_id 作为key
		//taskMap.M.Set("question_"+params.ToUserID+gconv.String(otherUserId), s)
		taskMap.M.Set("question_"+msg.Extra+gconv.String(otherUserId), s)
	}
}

// 邀请消息处理
func invitationMessage(msg dto.Msg, groupId string, otherUserId string, msgUId string) {
	if strings.Contains(msg.Content, initParam.InvitationMessageText) {
		timeout := gconv.String(time.Now().Add(initParam.InvitationTimeOut).Unix())
		s := taskMap.SendMap{
			FromUserID:     gtype.NewString(gconv.String(otherUserId)),
			ToUserID:       gtype.NewString(msg.Extra),
			TimeStamp:      gtype.NewInt(gconv.Int(timeout)),
			MsgUid:         gtype.NewString(msgUId),
			Type:           gtype.NewString("invitation"),
			PrivateGroupId: gtype.NewString(groupId),
		}
		//私聊群id+对方user_id 作为key
		//taskMap.M.Set("invitation_"+params.ToUserID+gconv.String(otherUserId), s)
		taskMap.M.Set("invitation_"+msg.Extra+gconv.String(otherUserId), s)
	}
}

//
func getCsUserOnline(router *gin.Context, uid int) int {
	redisCache := redisModules.ClientRedis.Get(initParam.CsUserStatusPrefix + gconv.String(uid))
	val, err := redisCache.Result()
	result := 0
	if err == nil {
		err = json.Unmarshal([]byte(val), &result)
		if err != nil {
			public.ContextNotice(router, "error", map[string]interface{}{
				"title": router.Request.URL,
				"err":   err,
			})
			return result
		}
	} else {
		if err != redis.Nil {
			public.ContextNotice(router, "error", map[string]interface{}{
				"title": router.Request.URL,
				"err":   err,
			})
			return result
		}
		err = addRedis(router, 1, initParam.CsUserStatusPrefix+gconv.String(uid))
		if err != nil {
			public.ContextNotice(router, "error", map[string]interface{}{
				"title": router.Request.URL,
				"err":   err,
			})
			return result
		}
	}
	return result
}

// 如果是客户发送的内容，则清除客服相关缓存
func clearCsCache(router *gin.Context, groupId string, adminUid int) {
	if adminUid != 0 {
		csUserOnlineStatus := getCsUserOnline(router, adminUid)
		if csUserOnlineStatus == 2 {
			key := "sendNoticeMsg_" + gconv.String(adminUid)
			timeout := gconv.String(time.Now().Add(initParam.SendNoticeTimeOut).Unix())
			if taskMap.M.Get(key) == nil {
				s := taskMap.SendMap{
					ToUserID:  gtype.NewString(gconv.String(adminUid)),
					TimeStamp: gtype.NewInt(gconv.Int(timeout)),
					Type:      gtype.NewString("sendNoticeMsg"),
				}
				taskMap.M.Set(key, s)
			}
		}
	}
	//
	key := groupId + "_customerService"
	if taskMap.M.Get(key) != nil {
		taskMap.M.Remove(key)
	}
}

//
func modifyUserOnline(router *gin.Context, queryParamSendMsg dto.User) error {
	paramJsonBytes, err := json.Marshal(queryParamSendMsg)
	if err != nil {
		return err
	}
	paramJsonStr := string(paramJsonBytes)
	queryUrl := initParam.BaseConf.Host + "/modifyuseronline"
	_, queryByte, err := lib.HttpJSON(lib.NewTrace(), queryUrl, paramJsonStr, 60*1000, nil)
	if err != nil {
		return err
	}
	resp := &middleware.Response{}
	if err := json.Unmarshal(queryByte, resp); err != nil {
		return err
	}
	if resp.ErrorCode != middleware.SUCCESS_CODE {
		public.ContextNotice(router, "debug", map[string]interface{}{
			"title": router.Request.URL,
			"msg":   fmt.Sprintf("queryParamSendMsg ==> %v", string(paramJsonBytes)),
		})
		return errors.New(resp.ErrorMsg)
	}
	return nil
}

// 如果是客服发送的内容，则清除客户相关缓存
func clearCCache(router *gin.Context, groupId string, fromUserId string) {
	{
		key := "sendNoticeMsg_" + fromUserId
		if taskMap.M.Get(key) != nil {
			taskMap.M.Remove(key)
		}
	}
	{
		csUserOnlineStatus := getCsUserOnline(router, gconv.Int(fromUserId))
		if csUserOnlineStatus == 0 || csUserOnlineStatus == 2 {
			err := modifyUserOnline(router, dto.User{
				PreMsg: dto.PreMsg{
					IsCustomerClient: 0,
				},
				IsOnline: 1,
				UserId:   fromUserId,
			})
			if err != nil {
				fmt.Println(err)
				glog.Error(err)
			}
		}
	}
	{
		key := "csOnline_" + gconv.String(fromUserId)
		timeout := gconv.String(time.Now().Add(initParam.CsOnlineTimeOut).Unix())
		s := taskMap.CsUserMap{
			Uid:       gtype.NewString(fromUserId),
			TimeStamp: gtype.NewInt(gconv.Int(timeout)),
		}
		taskMap.CsUser.Set(key, s)
	}
	{
		key := groupId + "_customer"
		if taskMap.M.Get(key) != nil {
			taskMap.M.Remove(key)
		}
	}
}

// 系统回复设置-客服无应答状态
func setCustomerServiceCache(cacheKey string, fromUserId string, groupId string, replySetting dto.ReplySetting) {
	timeout := gconv.String(time.Now().Add(time.Duration(replySetting.CustomerServiceTimeout) * time.Minute).Unix())
	if taskMap.M.Get(cacheKey) == nil {
		//构造发送结构
		s := taskMap.SendMap{
			FromUserID: gtype.NewString(fromUserId),
			ToUserID:   gtype.NewString(groupId),
			Content:    gtype.NewString(replySetting.CustomerServiceText),
			ChatType:   gtype.NewString("group"),
			TimeStamp:  gtype.NewInt(gconv.Int(timeout)),
			Type:       gtype.NewString("sendToCs"),
		}
		taskMap.M.Set(cacheKey, s)
	} else {
		sMap := taskMap.M.Get(cacheKey).(taskMap.SendMap)
		//最新的设置时间比旧的小，采用新的
		if sMap.TimeStamp.Val() > gconv.Int(timeout) {
			s := taskMap.SendMap{
				FromUserID: gtype.NewString(fromUserId),
				ToUserID:   gtype.NewString(groupId),
				Content:    gtype.NewString(replySetting.CustomerServiceText),
				ChatType:   gtype.NewString("group"),
				TimeStamp:  gtype.NewInt(gconv.Int(timeout)),
				Type:       gtype.NewString("sendToCs"),
			}
			taskMap.M.Set(cacheKey, s)
		}
	}
}

//
func dealRequestError(router *gin.Context, err error) {
	if err.Error() == initParam.DataIsNotExist {
		middleware.ResponseError(router, middleware.DATA_NOT_EXIST, err)
		return
	}
	middleware.ResponseError(router, middleware.INTERNAL_ERROR_CODE, err)
	return
}

//
func report(groupId string, online int) {
	redisCache := redisModules.ClientRedis.Get(initParam.ReportPrefix + groupId)
	val, err := redisCache.Result()
	result := dto.WkbImSessionReport{}
	if err == nil {
		err = json.Unmarshal([]byte(val), &result)
		if err != nil {
			return
		}
	}
	if result.CustomerUid > 0 {
		if online == 0 {
			result.Seconds = time.Now().Unix() - result.RecordTime + result.Seconds
		}
		result.RecordTime = time.Now().Unix()
		reportContent(groupId, result)
	}
}

//
func reportContent(groupId string, result dto.WkbImSessionReport) {
	jsonContent, err := json.Marshal(result)
	if err != nil {
		return
	}
	redisModules.ClientRedis.Set(initParam.ReportPrefix+groupId, jsonContent, 0)
}

//
func record(router *gin.Context, groupId string, state int) {
	redisCache := redisModules.ClientRedis.Get(initParam.ReportPrefix + groupId)
	val, err := redisCache.Result()
	result := dto.WkbImSessionReport{}
	if err == nil {
		err = json.Unmarshal([]byte(val), &result)
		if err != nil {
			return
		}
	}
	if result.CustomerUid > 0 {
		result.Seconds = time.Now().Unix() - result.RecordTime + result.Seconds
		//result.RecordTime = time.Now().Unix()
		err = serviceService.CreateReport(router, service.BusinessDB, &models.WkbImSessionReport{
			CustomerUid:         result.CustomerUid,
			CustomerNickname:    result.CustomerNickname,
			CustomerServiceUid:  result.CustomerServiceUid,
			CustomerServiceName: result.CustomerServiceName,
			Channel:             result.Channel,
			VisitStart:          result.VisitStart,
			VisitEnd:            time.Now().Format(initParam.TimeFormatYear),
			Seconds:             gconv.Int(result.Seconds),
			State:               state,
		})
		redisModules.ClientRedis.Del(initParam.ReportPrefix + groupId)
	}
}
